////////////////////////////////////////////////////////////////
///
/// File: ComplexTmpExp.m
/// Author: Lucio Davide Spano
/// Last update: 25 August 2012
/// Description: This file contains the definition of a n-ary
///              temporal expression
/// To do:
/// ------------------------------------------------------------
/// Copyright (c) 2012 Lucio Davide Spano
///                     (lucio.davide.spano@isti.cnr.it)
///
/// The use and distribution terms for this software are
/// contained in the file named license.txt, which can be found
/// in the root of this distribution.
/// By using this software in any fashion, you are agreeing to
/// be bound by the terms of this license.
///
/// You must not remove this notice, or any other, from this
/// software.
/// ------------------------------------------------------------
////////////////////////////////////////////////////////////////

#import "ComplexTmpExp.h"
#import "TmpExpProtected.h"
@class ExpEventArgs, Token;

@interface ComplexTmpExp (private)

- (int) findOperatorPosition:(TmpExp*) exp;
+ (NSNumber*) _getFalse;
+ (NSNumber*) _getTrue;
@end

@implementation ComplexTmpExp (private)

static NSNumber* _false = nil;
static NSNumber* _true = nil;

+ (NSNumber*) _getFalse{
    if(_false == nil){
        _false = [[NSNumber alloc] initWithBool:NO];
    }
    return _false;
}
+ (NSNumber*) _getTrue{
    if(_true == nil){
        _true =  [[NSNumber alloc] initWithBool:YES];
    }
    return _true;
}

- (int) findOperatorPosition:(TmpExp *)exp{
    int i = 0;
    for(TmpExp* t in self->operands){
        if(t == exp){
            return i;
        }
        i++;
    }
    return -1;
}

@end

@implementation ComplexTmpExp

- (id) init{
    self = [super init];
    if(self){
        self.type = Complex;
        self.tmpOperator = OrderIndependence;
        self->operands = [[NSMutableArray alloc] init];
        self->completion = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void) addOperand:(TmpExp *)operand{
    [self->operands addObject:operand];
    [self->completion addObject:[ComplexTmpExp _getFalse]];
    operand.parent = self;
    [operand addStateChangeListener:self];
}

- (void) removeOperand:(TmpExp *)operand{
    int position = [self findOperatorPosition:operand];
    if(position == -1){
        return;
    }
    [self->operands removeObject:operand];
    [self->completion removeObjectAtIndex:position];
    [operand removeStateChangeListener:self];
}

- (NSEnumerator*) getOperands{
    return [operands objectEnumerator];
}

- (TmpExp*) getOperand:(int)pos{
    if(pos >= 0 && pos < [self->operands count]){
        return [self->operands objectAtIndex:pos];
    }
    return nil;
}

- (void) setExecState:(int)_state :(Token *)_token{
    switch(self.tmpOperator){
        case OrderIndependence:{
            
            switch(_state){
                    
                case Enabled:{
                    if(self.state == Disabled ||
                       self.state == Performing){
                        for(int i= 0; i< self->completion.count; i++){
                            [self->completion replaceObjectAtIndex:i withObject:[ComplexTmpExp _getFalse]];
                            
                        }
                    }
                    for(TmpExp* op in self->operands){
                        if(op.state == Disabled || op.state == Completed || op.state == Error){
                            [op setExecState:Enabled :_token];
                        }
                    }
                    
                }break;
                    
                case Performing:
                    // nothing to do
                    break;
                    
                case Disabled:{
                    for(TmpExp* op in self->operands){
                        [op setExecState:Disabled :_token];
                    }
                    
                }break;
                    
                case Completed: {
                    if(self.iterative){
                        for(TmpExp* op in self->operands){
                            [op setExecState:Enabled :_token];
                        }
                    }
                }break;
            }
        }break;
            
        default: break;
            // the remaining operators are covered by te BinaryTmpExp class
    }
    
    [super setExecState:_state :_token];
}

- (void) onStateChanged:(TmpExp *)sender :(ExpEventArgs *)args :(Token *)_token{
    [self setExecState:sender.state :_token];
    
    switch(sender.state){
        case Completed:{
            int pos = [self findOperatorPosition:sender];
            if(pos >= 0){
                [self->completion replaceObjectAtIndex:pos withObject:[ComplexTmpExp _getTrue]];
                
                bool allDone = true;
                
                for(int i = 0; i< self->completion.count; i++){
                    allDone = allDone && [[self->completion objectAtIndex:i] boolValue];
                }
                
                if(allDone){
                    [self setExecState:Completed :_token];
                    [self raiseCompleted:self :args :_token];
                    [self raiseOnStateChanged:self :args :_token];
                }else{
                    [self setExecState:Performing :_token];
                    [self raisePerforming:self :args :_token];
                    [self raiseOnStateChanged:self :args :_token];
                }
            }
        }break;
            
        case Performing:{
            int pos = [self findOperatorPosition:sender];
            if(pos >=0){
                for(int i = 0; i < operands.count; i++){
                    // Disable the other operands if the
                    // user started to execute one operand
                    if(![[self->completion objectAtIndex:i] boolValue]){
                        TmpExp* op = [self->operands objectAtIndex:i];
                        [op setExecState:Disabled :_token];
                    }
                    [self setExecState:Performing :_token];
                    [self raisePerforming:sender :args :_token];
                    [self onStateChanged:sender :args :_token];
                }
            }
        }break;
            
        case Error:{
            [self setExecState:Error :_token];
            [self raiseError:self :args :_token];
            [self raiseOnStateChanged:self :args :_token];
            if(self.iterative && _token != nil){
                // avoid enabling null tokens
                [self setExecState:Enabled :_token];
            }
        }break;
            
            
        default: break;
    }
}

- (void) onNotAcceptedToken:(TmpExp *)sender :(SimpleTmpExp *)originalSender :(ExpEventArgs *)args :(Token *)token{
    [self raiseOnNotAcceptedToken:originalSender :args :token];
}

-(bool) accepts:(ExpEventArgs *)args{
            TmpExp* op = nil;
            for(int i = 0; i < operands.count; i++){
                op = [self->operands objectAtIndex:i];
                if((op.state == Enabled || op.state == Performing) &&[op accepts:args]){
                    return true;
                }
            }
            return false;
    
    return false;
}

@end
